package com.hmall.order.service.impl;


import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.feign.client.ItemClient;
import com.hmall.feign.client.UserClient;
import com.hmall.feign.pojo.Address;
import com.hmall.feign.pojo.Item;
import com.hmall.order.Constants.OrderConstants;
import com.hmall.order.Constants.RabbitConstants;
import com.hmall.order.Context.BaseContext;
import com.hmall.order.config.OrderServiceProperties;
import com.hmall.order.mapper.OrderDetailMapper;
import com.hmall.order.mapper.OrderLogisticsMapper;
import com.hmall.order.mapper.OrderMapper;
import com.hmall.order.pojo.Order;
import com.hmall.order.pojo.OrderDetail;
import com.hmall.order.pojo.OrderLogistics;
import com.hmall.order.service.IOrderService;
import com.hmall.order.web.request.RequestParams;

import io.seata.spring.annotation.GlobalTransactional;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;

import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Objects;


/**
 * @author Lewis
 * @date 2023/4/9 15:14
 * description order业务实现类
 */
@Service
@Slf4j
public class OrderService extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private UserClient userClient;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderLogisticsMapper orderLogisticsMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private OrderServiceProperties orderServiceProperties;

    /**
     * 新增订单
     *
     * @param params 前端请求参数
     * @return 订单id
     */
    @GlobalTransactional
    @Override
    public Long saveOrder(RequestParams params) {
        // 1-入参检查
        if (Objects.isNull(params)) {
            throw new RuntimeException("系统异常！");
        }
        // 1.1 获取用户id
        Long userId = BaseContext.getCurrentId();
        // 2-生成订单id
        Long orderId = IdWorker.getId();
        // 3-写入数据到order表和orderDetails表
        save2OrderAndOrderDetails(params, userId, orderId);
        // 4-写入数据到OrderLogistics表
        save2OrderLogistics(params.getAddressId(), orderId);
        // 5-调用item-service的减库存接口 下单扣库存
        boolean result = itemClient.deductStock(params.getItemId(), -params.getNum());
        // 5.1-库存数量检验
        if (!result) {
            throw new RuntimeException("订单库存不足");
        }
        // 5.2-发送延迟队列  延迟消息过期时间可以放到yaml中动态管理
        String delayTime = orderServiceProperties.getDelayTime();
        log.info("延迟时间为：{}", delayTime);
        sendDelayQueue(orderId.toString(), Long.valueOf(delayTime));
        // 6-返回订单id
        return orderId;
    }


    /**
     * 写入数据到OrderLogistics表
     *
     * @param addressId 地址id
     * @param orderId   订单id
     */
    private void save2OrderLogistics(Long addressId, Long orderId) {
        // 1-跨服务调用 根据用户id查询地址
        Address address = userClient.findAddressById(addressId);
        // 1.1-存在性校验
        if (Objects.isNull(address)) {
            throw new RuntimeException("系统异常");
        }
        // 2-将地址封装为OrderLogistics对象，写入tb_order_logistics表
        OrderLogistics orderLogistics = OrderLogistics.builder()
                .orderId(orderId)
                .logisticsNumber(RandomUtil.randomString(16))
                .logisticsCompany(RandomUtil.randomString(5))
                .contact(address.getContact())
                .mobile(address.getMobile())
                .province(address.getProvince())
                .city(address.getCity())
                .town(address.getTown())
                .street(address.getStreet())
                .build();
        orderLogisticsMapper.insert(orderLogistics);
    }


    /**
     * 写入数据到order表和orderDetails表
     *
     * @param params  前端请求参数
     * @param userId  用户id
     * @param orderId 订单id
     */
    private void save2OrderAndOrderDetails(RequestParams params, Long userId, Long orderId) {
        // 1-根据itemId跨服务调用itemService查询商品信息
        // 2 存在性校验
        if (Objects.isNull(params.getItemId())) {
            throw new RuntimeException("系统错误！");
        }
        // 3 跨服务调用
        Item item = itemClient.getItem(params.getItemId());
        // 存在性校验
        if (Objects.isNull(item)) {
            throw new RuntimeException("商品不存在！");
        }

        // 4-基于商品价格、购买数量计算商品总价：totalFee 单位分
        String totalFeeStr = BigDecimal.valueOf(item.getPrice()).toString();
        Long totalFee = Long.parseLong(totalFeeStr) * params.getNum();
        // 5-封装Order对象，初识status为未支付

        Order order = Order.builder()
                .status(OrderConstants.NON_PAYMENT)
                .id(orderId)
                .totalFee(totalFee)
                .paymentType(OrderConstants.DEFAULT_PAY)
                .userId(userId)
                .build();
        // 6-将Order写入数据库tb_order表中
        save(order);
        // 7-将商品信息、orderId信息封装为OrderDetail对象，写入tb_order_detail表
        OrderDetail orderDetail = OrderDetail.builder()
                .orderId(orderId)
                .itemId(item.getId())
                .num(params.getNum())
                .name(item.getName())
                .spec(item.getSpec())
                .price(item.getPrice())
                .image(item.getImage())
                .build();
        orderDetailMapper.insert(orderDetail);
    }


    /**
     * 发送延迟消息
     *
     * @param msg  待处理消息
     * @param time 延迟时间ms
     */
    public void sendDelayQueue(String msg, Long time) {
        // 创建消息
        Message message = MessageBuilder
                .withBody(msg.getBytes(StandardCharsets.UTF_8))
                .setHeader(RabbitConstants.DELAY_HEADER, time)
                .build();
        // 消息ID，需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(IdUtil.simpleUUID());
        // 发送消息
        rabbitTemplate.convertAndSend(RabbitConstants.EXCHANGE, RabbitConstants.ROUTING_KEY, message, correlationData);
        log.info("当前时间：{}，发送一条时长为{}ms的信息给延迟队列my.delayed.queue：{}",
                new Date().toString(), time, message);

    }

}
